﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
using Urb.Weather.Core.MeasureUnit;

namespace Urb.Weather.Core.Util
{
    public static class XmlResponseAnalyzerHelper
    {        
        private static readonly Tuple<string, IFormatProvider>[] dateFormats = 
        {
            new Tuple<string, IFormatProvider>("d MMM yyyy", new CultureInfo("en-US"))
        };

        public static XPathNavigator ValidateResponseParametersAndCreateNavigator(string response)
        {
            if (response == null)
                throw new ArgumentNullException("response");

            XmlDocument document = new XmlDocument();

            try
            {
                document.LoadXml(response);
            }
            catch (XmlException exception)
            {
                throw new ArgumentException("Wrong XML", "response", exception);
            }

            return document.CreateNavigator();
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "xpath")]
        public static void ReadSingleNode(this XPathNavigator navigator, string xpath, Action<string> setterAction)
        {
            if (navigator == null)
                throw new ArgumentNullException("navigator");

            if (xpath == null)
                throw new ArgumentNullException("xpath");
            
            if (setterAction == null)
                throw new ArgumentNullException("setterAction");

            XPathNavigator node = navigator.SelectSingleNode(xpath);

            if (node != null)
            {
                setterAction(node.Value);
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "xpath")]
        public static void ReadSingleNode(this XPathNavigator navigator, XmlNamespaceManager namespaceManager, string xpath, Action<string> setterAction)
        {
            if (navigator == null)
                throw new ArgumentNullException("navigator");

            if (namespaceManager == null)
                throw new ArgumentNullException("namespaceManager");

            if (xpath == null)
                throw new ArgumentNullException("xpath");

            if (setterAction == null)
                throw new ArgumentNullException("setterAction");

            XPathExpression expression = navigator.Compile(xpath);

            expression.SetContext(namespaceManager);

            XPathNavigator node = navigator.SelectSingleNode(expression);

            if (node != null)
            {
                setterAction(node.Value);
            }
        }

        public static TemperatureUnit? ConvertToTemperatureUnit(this string value)
        {
            if (string.IsNullOrEmpty(value))
                return null;

            switch (value.Trim().ToUpperInvariant())
            {
                case "C":
                    return TemperatureUnit.Celsius;
                case "F":
                    return TemperatureUnit.Fahrenheit;
            }

            return null;
        }

        public static DistanceUnit? ConvertToDistanceUnit(this string value)
        {
            if (string.IsNullOrEmpty(value))
                return null;

            switch (value.Trim().ToUpperInvariant())
            {
                case "KM":
                    return DistanceUnit.Kilometer;
                case "MI":
                    return DistanceUnit.Mile;
            }

            return null;
        }

        public static PressureUnit? ConvertToPressureUnit(this string value)
        {
            if (string.IsNullOrEmpty(value))
                return null;

            switch (value.Trim().ToUpperInvariant())
            {
                case "IN":
                    return PressureUnit.InchHg;
                case "MB":
                    return PressureUnit.Milibar;
            }

            return null;
        }

        public static SpeedUnit? ConvertToSpeedUnit(this string value)
        {
            if (string.IsNullOrEmpty(value))
                return null;

            switch (value.Trim().ToUpperInvariant())
            {
                case "KM/H":
                    return SpeedUnit.KilometerPerHour;
                case "MPH":
                    return SpeedUnit.MilePerHour;
            }

            return null;
        }

        public static int? ConvertToInt(this string value)
        {
            if (string.IsNullOrWhiteSpace(value))
                return null;

            int result;

            if (int.TryParse(value.Trim(), out result))
            {
                return result;
            }

            return null;
        }

        public static short? ConvertToShort(this string value)
        {
            if (string.IsNullOrWhiteSpace(value))
                return null;

            short result;

            if (short.TryParse(value.Trim(), out result))
            {
                return result;
            }

            return null;
        }

        public static byte? ConvertToByte(this string value)
        {
            if (string.IsNullOrWhiteSpace(value))
                return null;

            byte result;

            if (byte.TryParse(value.Trim(), out result))
            {
                return result;
            }

            return null;
        }

        public static decimal? ConvertToDecimal(this string value)
        {
            if (string.IsNullOrWhiteSpace(value))
                return null;

            decimal result;

            if (decimal.TryParse(value.Trim(), NumberStyles.Any, CultureInfo.InvariantCulture, out result))
            {
                return result;
            }

            return null;
        }

        public static DateTimeOffset? ConvertToDateTimeOffset(this string value)
        {
            if (string.IsNullOrWhiteSpace(value))
                return null;

            DateTimeOffset result;
            
            if (
                Rfc822DateTime.TryParse(
                    value.Trim(),
                    out result
                    )
                )
            {
                return result;
            }

            return null;
        }

        public static DayOfWeek? ConvertToDayOfWeek(this string value)
        {
            if (string.IsNullOrWhiteSpace(value))
                return null;

            switch (value.Trim())
            {
                case "Mon":
                    return DayOfWeek.Monday;
                case "Tue":
                    return DayOfWeek.Tuesday;
                case "Wed":
                    return DayOfWeek.Wednesday;
                case "Thu":
                    return DayOfWeek.Thursday;
                case "Fri":
                    return DayOfWeek.Friday;
                case "Sat":
                    return DayOfWeek.Saturday;
                case "Sun":
                    return DayOfWeek.Sunday;
            }

            return null;
        }

        public static DateTime? ConvertToDate(this string value)
        {
            if (string.IsNullOrWhiteSpace(value))
                return null;

            DateTime result;

            foreach(Tuple<string, IFormatProvider> dateFormat in dateFormats)
            {
                if (DateTime.TryParseExact(value, dateFormat.Item1, dateFormat.Item2, DateTimeStyles.AllowWhiteSpaces, out result))
                    return result;
            }           

            return null;
        }
    }
}
